#include "stdafx.h"
#include "SelectReactor.h"
#include "LFThreadPool.h"
#include "Guard.h"

namespace Network
{
	ThreadMutex iVectorSync; //this is used to synchronize access to non-thread safe <vector>
	ThreadMutex iInstanceSync;
	SelectReactor* SelectReactor::instance_ = NULL;
	
	SelectReactor* SelectReactor::instance(void)
	{
		Guard guard(iInstanceSync);
		if(instance_ == NULL)
		{
			instance_ = new SelectReactor();

			if(instance_ == NULL){
				printf("Cannot create SelectReactor instance ! Aborting\n");
				throw;
			}
		}

		return instance_;
	}

	SelectReactor::SelectReactor(void)
	{
		//init;
		//cannot refer to an instance, before it is created by _new_, i.e. cannot use the constructor
		//handlers = (HandlerMap*)(malloc(sizeof(HandlerMap)));
		//handlers->count = 0;

		demultiplexer = new EventDemultiplex();
	}
	
	SelectReactor::SelectReactor(EventDemultiplex* _demultiplexer)
	{
		//instance_->handlers = (HandlerMap*)(malloc(sizeof(HandlerMap)));
		instance_->demultiplexer = _demultiplexer;
		//instance_->handlers->count = 0;
	}

	SelectReactor::~SelectReactor() {
		//free(instance_->handlers);	
	}

	void SelectReactor::register_handler(EventHandler* eh, EventType et){
		Guard guard(iVectorSync);

		EventTuple *tempEventTupleVector = new EventTuple();
		tempEventTupleVector->eventHandler = eh;
		tempEventTupleVector->eventType = et;
		tempEventTupleVector->isActive = true;
		if(!inEventTuple(tempEventTupleVector))
		{
			eventTuple.push_back(tempEventTupleVector);
		}
	}

	void SelectReactor::remove_handler(EventHandler* eh, EventType et)
	{
		Guard guard(iVectorSync);

		EventTuple *tempEventTupleVector = new EventTuple();
		tempEventTupleVector->eventHandler = eh;
		tempEventTupleVector->eventType = et;
		
		for(unsigned int i = 0; i <eventTuple.size(); i++)
		{
			if(tempEventTupleVector->eventHandler == eventTuple.at(i)->eventHandler && tempEventTupleVector->eventType == eventTuple.at(i)->eventType)
			{
				EventTuple *memCleanUp = eventTuple.at(i);
				eventTuple.erase(eventTuple.begin()+i);
				delete memCleanUp;
				break;
			}
		}
	}

	void SelectReactor::suspend_handle(HANDLE h, EventType et)
	{
		Guard guard(iVectorSync);
		for(unsigned int i = 0; i < eventTuple.size(); i++)
		{
			if( eventTuple.at(i)->eventHandler->GetHandle() == h && eventTuple.at(i)->eventType == et)
			{
				eventTuple.at(i)->isActive = false;
				break;
			}
		}
	}
			
	void SelectReactor::resume_handle(HANDLE h, EventType et)
	{
		Guard guard(iVectorSync);
		for(unsigned int i = 0; i < eventTuple.size(); i++) 
		{
			if( eventTuple.at(i)->eventHandler->GetHandle() == h && eventTuple.at(i)->eventType == et )
			{
				eventTuple.at(i)->isActive = true;
				break;
			}
		}
	}

	bool SelectReactor::inEventTuple(EventTuple* tuple)
	{
		for(u_int i = 0; i < eventTuple.size(); i++)
		{
			if(tuple->eventHandler== eventTuple.at(i)->eventHandler && tuple->eventType == eventTuple.at(i)->eventType)
			{
				return true;
			} 
			/*else
			{
				return false;
			}*/
		}

		return false;
	}

	void SelectReactor::handle_events(timeval *timeout)
	{
		ConvertFDsToSets(readFDs, writeFDs, exceptFDs);

		int result = demultiplexer->select(FD_SETSIZE, &readFDs, &writeFDs, &exceptFDs, timeout);
		
		if (result == SOCKET_ERROR)
		{
			printf("ERROR: %d\n", WSAGetLastError());
		}else if (result == 0)
		{
			printf("Client disconnected\n");
		}else
		{
			Guard guard (iVectorSync); //synchronize for the std::vector
			std::vector<EventTuple*> *tempEventTupleVector = new std::vector<EventTuple*>;
			u_int size = eventTuple.size();
			for(u_int i = 0; i < size; i++)
			{
				EventTuple *tuple = new EventTuple();
				tuple->eventHandler = eventTuple.at(i)->eventHandler;
				tuple->eventType = eventTuple.at(i)->eventType;
				tempEventTupleVector->push_back(tuple);
			}
			
			for (int i = 0; i < (int)tempEventTupleVector->size() && i <= FD_SETSIZE-1 && result > 0; i++) 
			{
				EventHandler *handlerUpcoming = tempEventTupleVector->at(i)->eventHandler;
				HANDLE handle = NULL;
				
				if(handlerUpcoming != NULL) {
					handle = handlerUpcoming->GetHandle();
				}

				if (handle != NULL && FD_ISSET(handle, &readFDs)) 
				{

					try
					{
						EventType e = static_cast<EventType>(READ_EVENT);
						handlerUpcoming->HandleEvent(handle, e);
					}
					catch(...)
					{
						//printf("Client disconnected\n");
						EventType e = static_cast<EventType>(READ_EVENT);
						this->remove_handler(handlerUpcoming, e);
						delete handlerUpcoming;
					}
					result--;
				} 
				else if (FD_ISSET(handle, &writeFDs)) 
				{
					try
					{
						EventType e = static_cast<EventType>(WRITE_EVENT);
						handlerUpcoming->HandleEvent(handle, e);
					}
					catch(...)
					{
						//std::cout << "Client disconnected" << std::endl;
						EventType e = static_cast<EventType>(WRITE_EVENT);
						this->remove_handler( handlerUpcoming, e);
						delete handlerUpcoming;
					}
					result--;
				} 
				else if (FD_ISSET(handle, &exceptFDs) ) 
				{
					result--;
				}	
				guard.Release();
			}

			//Memory clean-up
			guard.Acquire(); //synchronize for the std::vector
			for(u_int i = 0; i < tempEventTupleVector->size(); i++)
			{
				EventTuple *temp = tempEventTupleVector->at(i);
				delete temp;

				tempEventTupleVector->clear();
			}
			guard.Release();
		}
	}

	void SelectReactor::ConvertFDsToSets(fd_set &readFDs, fd_set &writeFDs, fd_set &exceptFDs)
	{
		FD_ZERO(&readFDs);
		FD_ZERO(&writeFDs);
		FD_ZERO(&exceptFDs);
		for (u_int i = 0; i < eventTuple.size(); i++) 
		{
			SOCKET currentSocket = (SOCKET)eventTuple.at(i)->eventHandler->GetHandle();
			EventType currentEventType = eventTuple.at(i)->eventType;
			bool isActive = eventTuple.at(i)->isActive;

			if(currentEventType == READ_EVENT && isActive == true)
			{
				FD_SET(currentSocket, &readFDs);
			}
			else if (currentEventType == WRITE_EVENT && isActive == true)
			{
				FD_SET(currentSocket, &writeFDs);
			}
			else if (currentEventType == TIMEOUT_EVENT && isActive == true)
			{
				FD_SET(currentSocket, &exceptFDs);
			}
		}
	}
}